package step

import (
	"errors"
	"github.com/wanchain/go-mpc/common/hexutil"
	"github.com/wanchain/go-mpc/log"
	"github.com/wanchain/go-mpc/p2p/discover"
	"github.com/wanchain/go-mpc/storeman/osmconf"
	"github.com/wanchain/go-mpc/storeman/schnorrmpc"
	mpccrypto "github.com/wanchain/go-mpc/storeman/storemanmpc/crypto"
	"github.com/wanchain/go-mpc/storeman/storemanmpc/protocol"
	mpcprotocol "github.com/wanchain/go-mpc/storeman/storemanmpc/protocol"
	"math/big"
)

type lagrangeGeneratorUni struct {
	BaseGenerator
	seed        big.Int
	message     map[discover.NodeID]big.Int
	result      big.Int
	preValueKey string
	grpIdString string
	smcer       mpcprotocol.SchnorrMPCer
	ContextId   uint64
}

func createLagrangeGeneratorUni(preValueKey string) *lagrangeGeneratorUni {
	return &lagrangeGeneratorUni{message: make(map[discover.NodeID]big.Int), preValueKey: preValueKey}
}

func (lag *lagrangeGeneratorUni) initialize(totalNum int, result mpcprotocol.MpcResultInterface) error {
	log.SyslogDebug("lagrangeGeneratorUni::initialize begin", "ctx", lag.GetContextId())

	// get self rpkshare
	_, grpIdString, _ := osmconf.GetGrpId(result)

	lag.grpIdString = grpIdString

	value, err := result.GetValue(lag.preValueKey)
	if err != nil {
		return err
	}

	lag.seed = value[0]

	log.SyslogDebug("lagrangeGeneratorUni::initialize succeed", "ctx", lag.GetContextId())
	return nil
}

func (lag *lagrangeGeneratorUni) calculateResult() error {
	log.SyslogDebug("lagrangeGeneratorUni::calculateResult begin", "ctx", lag.GetContextId())

	f := []big.Int{}
	seed := []big.Int{}
	var indexes []big.Int
	smpcer := schnorrmpc.NewSkSchnorrMpc()
	var nodeIds []*discover.NodeID
	for nodeId, value := range lag.message {
		nodeIdBytes := nodeId
		nodeIds = append(nodeIds, &nodeIdBytes)
		f = append(f, value)
		xValue, err := osmconf.GetOsmConf().GetXValueByNodeId(lag.grpIdString, &nodeId, smpcer)
		if err != nil {
			log.SyslogErr("lagrangeGeneratorUni::calculateResult", "calculateResult", err.Error(), "ctx", lag.GetContextId())
			return err
		}
		seed = append(seed, *xValue)
		index, _ := osmconf.GetOsmConf().GetInxByNodeId(lag.grpIdString, &nodeId)
		indexBig := big.NewInt(0).SetUint64(uint64(index))
		indexes = append(indexes, *indexBig)
	}

	bigCollection, _ := osmconf.BuildDataByIndexes(&indexes)
	log.SyslogInfo("lagrangeGeneratorUni::calculateResult", "len(peer)", len(nodeIds),
		"calculateResult peers", discover.ArrNodeIdpsToStr(nodeIds),
		"bigcollection", hexutil.Encode(bigCollection.Bytes()),
		"ctx", lag.GetContextId())

	lag.result = mpccrypto.Lagrange(f, seed)

	threshold, _ := osmconf.GetOsmConf().GetThresholdNum(lag.grpIdString)
	if threshold < uint16(1) {
		log.SyslogErr("lagrangeGeneratorUni::calculateResult threshold is lesser 1", "ctx", lag.GetContextId())
		return errors.New("lagrangeGeneratorUni::calculateResult threshold is lesser 1")
	}

	log.SyslogInfo("lagrangeGeneratorUni::calculateResult", "len(peer)", len(nodeIds),
		"calculateResult peers", discover.ArrNodeIdpsToStr(nodeIds),
		"ctx", lag.GetContextId(),
		"preValueKey", lag.preValueKey,
		"Need nodes number:", threshold,
		"Now nodes number:", len(seed))
	if len(seed) < int(threshold) {
		return protocol.ErrTooLessDataCollected
	}

	lag.result = mpccrypto.Lagrange(f, seed)

	log.SyslogInfo("lagrangeGeneratorUni::calculateResult", "ARResult", hexutil.Encode(lag.result.Bytes()),
		"ctx", lag.GetContextId(),
		"preValueKey", lag.preValueKey)

	log.SyslogDebug("lagrangeGeneratorUni::calculateResult succeed", "ctx", lag.GetContextId())
	return nil
}

func (lag *lagrangeGeneratorUni) SetSchnorrMpcer(smcer mpcprotocol.SchnorrMPCer) error {
	lag.smcer = smcer
	return nil
}

func (lag *lagrangeGeneratorUni) SetContextId(contextId uint64) {
	lag.ContextId = contextId
}

func (lag *lagrangeGeneratorUni) GetContextId() uint64 {
	return lag.ContextId
}

func (lag *lagrangeGeneratorUni) SetMpcContext(mpc MpcContextFunc) {
	lag.mpc = mpc
}

func (lag *lagrangeGeneratorUni) GetMpcContext() MpcContextFunc {
	return lag.mpc
}
